#include <Globals.h>
#include <Timer.h>
#include <Asm.h>
#include <MonitorDriver.h>
#include <ProcessManager.h>

Timer::Timer()
 : InterruptServiceRoutine()
{
  tick = 0;
  frequency = 0;
  // Initialise the resource and functions.
  resource = new Resource((Callable*)this, "Timer");
  resource->add((Resource::Call0)&Timer::getTick, Timer_IF::getTick);
}

Timer::~Timer()
{
}

//
// init -- Sends the hardware signals to start the PIT
//         interrupting at freq Hertz.
//
void Timer::init(u32int freq)
{

  // We can't divide by zero, catch stupid values here.
  ASSERT(freq > 0);

  frequency = freq;
  phase     = 1000 / frequency; // set the phase in milliseconds.
  
  u32int divisor = 1193180 / frequency;
  
  // The divisor has to fit into a 16-bit register. Check we aren't overflowing
  ASSERT((divisor & 0xFFFF0000) == 0);
  
  // Send command byte (see http://www.osdever.net/bkerndev/index.php)
  outb(0x43, 0x36);
  
  // Divisor has to be sent byte-wise, so split here into upper/lower bytes.
  u8int l = (u8int)(divisor & 0xFF);
  u8int h = (u8int)( (divisor>>8) & 0xFF );
  
  // Send the frequency divisor
  outb(0x40, l);
  outb(0x40, h);
  
}

//
// run -- Called by the IRQ handler when our interrupt comes in.
//
void Timer::run(Registers *r)
{

  if (r->int_no >= 40)
  {
    // Send reset signal to slave.
    outb(0xA0, 0x20);
  }
  // Send reset signal to master. (As well as slave, if necessary).
  outb(0x20, 0x20);
  
  // Check our init method has actually been called.
  ASSERT(frequency > 0);

  tick += phase;
  
  // TODO:: every so often sync with the hardware clock.
  
  processManager->shortTermSchedule();
}
